package com.example.studyspot.util;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.example.studyspot.config.MinIOConfigProperties;
import io.minio.*;
import io.minio.errors.*;
import io.minio.http.Method;
import io.minio.messages.*;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.lang3.StringUtils;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.io.RandomAccessRead;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.poi.hslf.usermodel.HSLFSlide;
import org.apache.poi.hslf.usermodel.HSLFSlideShow;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StreamUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
import java.util.stream.StreamSupport;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Slf4j
@Component
public class MinioUtil {
    @Autowired
    private MinioClient minioClient;

    @Autowired
    private MinioAsyncClient minioAsyncClient;

    @Autowired
    private MinIOConfigProperties minIOConfigProperties;

    @Autowired
    private String separator;

    /**
     * 检查存储桶是否存在
     *
     * @param bucketName 存储桶名称
     * @return 如果存储桶存在返回true，否则返回false
     */
    @SneakyThrows
    public boolean bucketExists(String bucketName) {
        var found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        log.info("存储桶: {}, {}", bucketName, found ? "存在" : "不存在");
        return found;
    }

    /**
     * 列出所有存储桶名称
     *
     * @return 存储桶名称列表
     */
    @SneakyThrows
    public List< String > listBucketNames() {
        return minioClient.listBuckets().stream().map(Bucket::name).toList();
    }

    /**
     * 列出所有存储桶
     *
     * @return 存储桶列表
     */
    @SneakyThrows
    public List< Bucket > listBuckets() {
        return minioClient.listBuckets();
    }

    /**
     * 列出存储桶中的所有对象
     *
     * @param bucketName 存储桶名称
     * @return 对象列表
     */
    @SneakyThrows
    public List< String > listObject(String bucketName) {
        if (!bucketExists(bucketName)) {
            log.warn("存储桶不存在: {}", bucketName);
            return List.of();
        }
        ListObjectsArgs listObjectsArgs = ListObjectsArgs.builder()
                .bucket(bucketName)
                .recursive(true)
                .build();

        return StreamSupport.stream(minioClient.listObjects(listObjectsArgs).spliterator(), false)
                .map(result -> {
                    try {
                        Item item = result.get();
                        return item.isDir() ? null : item.objectName();
                    } catch (Exception e) {
                        log.error("处理对象时发生错误", e);
                        return null;
                    }
                })
                .filter(Objects::nonNull)
                .toList();
    }

    /**
     * 列出存储桶中的所有对象名称
     *
     * @param bucketName 存储桶名称
     * @return 对象名称列表
     */
    @SneakyThrows
    public List< String > listObjectNames(String bucketName) {
        if (bucketExists(bucketName)) {
            return StreamSupport.stream(minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).build()).spliterator(), false)
                    .map(this::getObjectName)
                    .toList();
        }
        log.warn("存储桶不存在: {}", bucketName);
        return List.of();
    }

    /**
     * 查询单个存储桶
     *
     * @param bucketName 存储桶名称
     * @return 存储桶信息
     */
    @SneakyThrows
    public Map< String, Object > getBucketInfo(String bucketName) {
        if (!bucketExists(bucketName)) {
            log.warn("存储桶不存在: {}", bucketName);
            return null;
        }

        Map< String, Object > bucketInfo = new HashMap<>();
        bucketInfo.put("bucketName", bucketName);

        // 获取存储桶创建时间
        try {
            minioClient.listBuckets().stream()
                    .filter(b -> b.name().equals(bucketName))
                    .findFirst().ifPresent(bucket -> bucketInfo.put("creationDate", bucket.creationDate()));
        } catch (Exception e) {
            log.error("获取存储桶创建时间失败", e);
        }

        // 获取存储桶文件数量
        try {
            long fileCount = minioClient.listObjects(ListObjectsArgs.builder()
                    .bucket(bucketName)
                    .recursive(true)
                    .build()).spliterator().estimateSize();
            bucketInfo.put("fileCount", fileCount);
        } catch (Exception e) {
            log.error("获取存储桶文件数量失败", e);
        }

        return bucketInfo;
    }

    /**
     * 分页查询存储桶（支持模糊查询）
     *
     * @param prefix 存储桶名称前缀（用于模糊查询）
     * @param page   当前页码
     * @param size   每页大小
     * @return 分页结果
     */
    @SneakyThrows
    public Map< String, Object > listBucketsWithPagination(String prefix, int page, int size) {
        List< Map< String, Object > > bucketList = new ArrayList<>();
        List< Bucket > allBuckets = minioClient.listBuckets();

        // 过滤存储桶名称
        for (Bucket bucket : allBuckets) {
            if (prefix == null || bucket.name().contains(prefix)) {
                Map< String, Object > bucketInfo = new HashMap<>();
                bucketInfo.put("bucketName", bucket.name());
                bucketInfo.put("creationDate", bucket.creationDate());
                bucketList.add(bucketInfo);
            }
        }

        // 分页逻辑
        int total = bucketList.size();
        int start = (page - 1) * size;
        int end = Math.min(start + size, total);
        List< Map< String, Object > > paginatedBuckets = bucketList.subList(start, end);

        // 返回分页结果
        Map< String, Object > result = new HashMap<>();
        result.put("data", paginatedBuckets);
        result.put("total", total);
        result.put("pageSize", size);
        result.put("pages", (int) Math.ceil((double) total / size));
        result.put("page", page);
        return result;
    }


    @SneakyThrows
    private String getObjectName(Result< Item > result) {
        return result.get().objectName();
    }


    /**
     * 创建存储桶
     *
     * @param bucketName 存储桶名称
     * @return 如果成功创建存储桶返回true，如果存储桶已存在返回false
     */
    @SneakyThrows
    public boolean saveBucket(String bucketName) {
        if (!bucketExists(bucketName)) {
            minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            log.info("成功创建存储桶：{}", bucketName);
            return true;
        } else {
            log.info("存储桶已存在: {}", bucketName);
            return false;
        }
    }

    /**
     * 创建多个存储桶
     *
     * @param bucketNames 存储桶名称列表
     * @return 是否成功
     */
    @SneakyThrows
    public boolean saveBucket(List< String > bucketNames) {
        for (String bucketName : bucketNames) {
            if (bucketExists(bucketName)) {
                log.warn("存储桶已存在: {}", bucketName);
                return false;
            }
            try {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
                log.info("存储桶创建成功: {}", bucketName);
            } catch (Exception e) {
                log.error("存储桶创建失败: {}", bucketName, e);
                return false;
            }
        }
        return true;
    }

    /**
     * 修改存储桶名称
     *
     * @param oldBucketName 旧存储桶名称
     * @param newBucketName 新存储桶名称
     * @return 是否成功
     */
    @SneakyThrows
    public boolean renameBucket(String oldBucketName, String newBucketName) {
        if (bucketExists(oldBucketName)) {
            log.warn("存储桶不存在: {}", oldBucketName);
            return false;
        } else {
            // 检查新存储桶是否已存在
            if (bucketExists(newBucketName)) {
                log.warn("目标存储桶已存在: {}", newBucketName);
                return false;
            } else {
                try {
                    // 创建新存储桶
                    minioClient.makeBucket(MakeBucketArgs.builder().bucket(newBucketName).build());

                    // 复制所有文件到新存储桶
                    Iterable<Result<Item>> results = minioClient.listObjects(
                            ListObjectsArgs.builder()
                                    .bucket(oldBucketName)
                                    .recursive(true)
                                    .build());

                    for (Result<Item> result : results) {
                        Item item = result.get();
                        minioClient.copyObject(
                                CopyObjectArgs.builder()
                                        .bucket(newBucketName)
                                        .object(item.objectName())
                                        .source(CopySource.builder()
                                                .bucket(oldBucketName)
                                                .object(item.objectName())
                                                .build())
                                        .build());
                    }

                    // 删除旧存储桶
                    minioClient.removeBucket(RemoveBucketArgs.builder().bucket(oldBucketName).build());

                    log.info("存储桶重命名成功: {} -> {}", oldBucketName, newBucketName);
                    return true;
                } catch (Exception e) {
                    log.error("存储桶重命名失败: {}", oldBucketName, e);
                    return false;
                }
            }
        }
    }

    /**
     * 批量修改存储桶名称
     *
     * @param oldBucketNames 旧存储桶名称列表
     * @param newBucketNames 新存储桶名称列表
     * @return 是否成功
     */
    @SneakyThrows
    public boolean renameBucket(List< String > oldBucketNames, List< String > newBucketNames) {
        if (oldBucketNames.size() != newBucketNames.size()) {
            log.warn("旧存储桶名称列表和新存储桶名称列表长度不一致");
            return false;
        }

        for (int i = 0; i < oldBucketNames.size(); i++) {
            String oldBucketName = oldBucketNames.get(i);
            String newBucketName = newBucketNames.get(i);

            if (!bucketExists(oldBucketName)) {
                log.warn("存储桶不存在: {}", oldBucketName);
                return false;
            }

            if (bucketExists(newBucketName)) {
                log.warn("目标存储桶已存在: {}", newBucketName);
                return false;
            }

            try {
                // 创建新存储桶
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(newBucketName).build());

                // 复制所有文件到新存储桶
                Iterable< Result< Item > > results = minioClient.listObjects(
                        ListObjectsArgs.builder()
                                .bucket(oldBucketName)
                                .recursive(true)
                                .build());

                for (Result< Item > result : results) {
                    Item item = result.get();
                    minioClient.copyObject(
                            CopyObjectArgs.builder()
                                    .bucket(newBucketName)
                                    .object(item.objectName())
                                    .source(CopySource.builder()
                                            .bucket(oldBucketName)
                                            .object(item.objectName())
                                            .build())
                                    .build());
                }

                // 删除旧存储桶
                minioClient.removeBucket(RemoveBucketArgs.builder().bucket(oldBucketName).build());

                log.info("存储桶重命名成功: {} -> {}", oldBucketName, newBucketName);
            } catch (Exception e) {
                log.error("存储桶重命名失败: {}", oldBucketName, e);
                return false;
            }
        }

        return true;
    }

    /**
     * 拷贝存储桶
     *
     * @param srcBucketName 源存储桶名称
     * @param destBucketName 目标存储桶名称
     * @return 如果成功返回true，否则返回false
     */
    @SneakyThrows
    public boolean copyBucket(String srcBucketName, String destBucketName) {
        if (bucketExists(srcBucketName)) {
            log.warn("源存储桶不存在: {}", srcBucketName);
            return false;
        } else {
            if (bucketExists(destBucketName)) {
                log.warn("目标存储桶已存在: {}", destBucketName);
                return false;
            } else {
                try {
                    // 创建目标存储桶
                    minioClient.makeBucket(MakeBucketArgs.builder().bucket(destBucketName).build());
                    log.info("创建目标存储桶成功: {}", destBucketName);

                    // 列出源存储桶中的所有对象
                    Iterable<Result<Item>> results = minioClient.listObjects(
                            ListObjectsArgs.builder()
                                    .bucket(srcBucketName)
                                    .recursive(true)
                                    .build());

                    // 复制所有对象到新存储桶
                    for (Result<Item> result : results) {
                        Item item = result.get();
                        minioClient.copyObject(
                                CopyObjectArgs.builder()
                                        .bucket(destBucketName)
                                        .object(item.objectName())
                                        .source(CopySource.builder()
                                                .bucket(srcBucketName)
                                                .object(item.objectName())
                                                .build())
                                        .build());
                    }

                    log.info("存储桶复制成功: {} -> {}", srcBucketName, destBucketName);
                    return true;
                } catch (Exception e) {
                    log.error("存储桶复制失败: {}", srcBucketName, e);
                    return false;
                }
            }
        }
    }

    /**
     * 批量拷贝存储桶
     *
     * @param srcBucketNames 源存储桶名称列表
     * @param destBucketNames 目标存储桶名称列表
     * @return 如果所有存储桶都成功复制返回true，否则返回false
     */
    @SneakyThrows
    public boolean copyBucket(List<String> srcBucketNames, List<String> destBucketNames) {
        if (srcBucketNames.size() != destBucketNames.size()) {
            log.warn("源存储桶名称列表和目标存储桶名称列表长度不一致");
            return false;
        } else {
            for (int i = 0; i < srcBucketNames.size(); i++) {
                String srcBucketName = srcBucketNames.get(i);
                String destBucketName = destBucketNames.get(i);

                if (!copyBucket(srcBucketName, destBucketName)) {
                    return false;
                }
            }

            log.info("批量复制存储桶完成");
            return true;
        }
    }


    /**
     * 删除存储桶
     *
     * @param bucketName 存储桶名称
     * @return 如果成功删除存储桶返回true，否则返回false
     */
    @SneakyThrows
    public boolean removeBucket(String bucketName) {
        if (bucketExists(bucketName)) {
            var objects = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).build());
            if (!objects.iterator().hasNext()) {
                minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
                log.info("成功删除存储桶：{}", bucketName);
                return true;
            } else {
                log.warn("无法删除非空存储桶：{}", bucketName);
                return false;
            }
        }
        log.warn("存储桶不存在: {}", bucketName);
        return false;
    }

    /**
     * 批量删除存储桶
     *
     * @param bucketNames 存储桶名称列表
     * @return 是否成功
     */
    @SneakyThrows
    public boolean removeBucket(List< String > bucketNames) {
        for (String bucketName : bucketNames) {
            if (!bucketExists(bucketName)) {
                log.warn("存储桶不存在: {}", bucketName);
                return false;
            }

            try {
                minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
                log.info("存储桶删除成功: {}", bucketName);
            } catch (Exception e) {
                log.error("存储桶删除失败: {}", bucketName, e);
                return false;
            }
        }
        return true;
    }

    /**
     * 设置存储桶访问策略
     *
     * @param bucketName 存储桶名称
     * @param policy     策略内容（JSON格式字符串）
     * @return 如果成功返回true，否则返回false
     */
    @SneakyThrows
    public boolean setBucketPolicy(String bucketName, String policy) {
        if (!bucketExists(bucketName)) {
            log.warn("存储桶不存在: {}", bucketName);
            return false;
        }
        minioClient.setBucketPolicy(SetBucketPolicyArgs.builder()
                .bucket(bucketName)
                .config(policy)
                .build());
        return true;
    }

    /**
     * 获取存储桶访问策略
     *
     * @param bucketName 存储桶名称
     * @return 策略内容（JSON格式字符串），如果获取失败返回null
     */
    @SneakyThrows
    public String getBucketPolicy(String bucketName) {
        if (!bucketExists(bucketName)) {
            log.warn("存储桶不存在: {}", bucketName);
            return null;
        }
        return minioClient.getBucketPolicy(GetBucketPolicyArgs.builder()
                .bucket(bucketName)
                .build());
    }

    /**
     * 开启存储桶版本控制
     *
     * @param bucketName 存储桶名称
     * @return 如果成功返回true，否则返回false
     */
    @SneakyThrows
    public boolean enableBucketVersioning(String bucketName) {
        if (!bucketExists(bucketName)) {
            log.warn("存储桶不存在: {}", bucketName);
            return false;
        }
        minioClient.setBucketVersioning(SetBucketVersioningArgs.builder()
                .bucket(bucketName)
                .config(new VersioningConfiguration(VersioningConfiguration.Status.ENABLED, null))
                .build()
        );
        return true;
    }

    /**
     * 关闭存储桶版本控制
     *
     * @param bucketName 存储桶名称
     * @return 如果成功返回true，否则返回false
     */
    @SneakyThrows
    public boolean disableBucketVersioning(String bucketName) {
        if (!bucketExists(bucketName)) {
            log.warn("存储桶不存在: {}", bucketName);
            return false;
        }
        minioClient.setBucketVersioning(SetBucketVersioningArgs.builder()
                .bucket(bucketName)
                .config(new VersioningConfiguration(VersioningConfiguration.Status.SUSPENDED, null))
                .build()
        );
        return true;
    }


    /**
     * 查询单个文件夹
     *
     * @param bucketName 存储桶名称
     * @param folderName 文件夹名称
     * @return 文件夹信息
     */
    @SneakyThrows
    public Map< String, Object > getFolderInfo(String bucketName, String folderName) {
        if (!bucketExists(bucketName)) {
            log.warn("存储桶不存在: {}", bucketName);
            return null;
        }

        List< Map< String, Object > > files = new ArrayList<>();
        Iterable< Result< Item > > results = minioClient.listObjects(
                ListObjectsArgs.builder()
                        .bucket(bucketName)
                        .prefix(folderName)
                        .recursive(false)
                        .build());

        for (Result< Item > result : results) {
            Item item = result.get();
            if (!item.isDir()) {
                Map< String, Object > fileInfo = new HashMap<>();
                fileInfo.put("objectName", item.objectName());
                fileInfo.put("size", item.size());
                fileInfo.put("lastModified", item.lastModified());
                files.add(fileInfo);
            }
        }

        Map< String, Object > folderInfo = new HashMap<>();
        folderInfo.put("folderName", folderName);
        folderInfo.put("files", files);
        return folderInfo;
    }

    /**
     * 上传包含文件的文件夹
     *
     * @param bucketName 存储桶名称
     * @param folderName 文件夹名称（包含路径）
     * @param files      文件列表（文件名 -> 文件流）
     * @return 是否成功
     */
    @SneakyThrows
    public boolean uploadFolderWithFile(String bucketName, String folderName, Map< String, InputStream > files) {
        if (!bucketExists(bucketName)) {
            log.warn("存储桶不存在: {}", bucketName);
            return false;
        }

        for (Map.Entry< String, InputStream > entry : files.entrySet()) {
            String fileName = entry.getKey();
            InputStream stream = entry.getValue();
            String objectName = FileUtil.normalize(folderName + "/" + fileName);

            try {
                minioClient.putObject(
                        PutObjectArgs.builder()
                                .bucket(bucketName)
                                .object(objectName)
                                .stream(stream, -1, 10485760) // 10MB 分片
                                .build());
                log.info("文件上传成功: {}", objectName);
            } catch (Exception e) {
                log.error("文件上传失败: {}", objectName, e);
                return false;
            }
        }

        return true;
    }

    /**
     * 批量上传包含文件的文件夹
     *
     * @param bucketName 存储桶名称
     * @param folders    文件夹列表（文件夹名称 -> 文件列表）
     * @return 是否成功
     */
    @SneakyThrows
    public boolean uploadFoldersWithFile(String bucketName, Map< String, Map< String, InputStream > > folders) {
        if (!bucketExists(bucketName)) {
            log.warn("存储桶不存在: {}", bucketName);
            return false;
        }

        for (Map.Entry< String, Map< String, InputStream > > folderEntry : folders.entrySet()) {
            String folderName = folderEntry.getKey();
            Map< String, InputStream > files = folderEntry.getValue();

            for (Map.Entry< String, InputStream > fileEntry : files.entrySet()) {
                String fileName = fileEntry.getKey();
                InputStream stream = fileEntry.getValue();
                String objectName = FileUtil.normalize(folderName + "/" + fileName);

                try {
                    minioClient.putObject(
                            PutObjectArgs.builder()
                                    .bucket(bucketName)
                                    .object(objectName)
                                    .stream(stream, -1, 10485760) // 10MB 分片
                                    .build());
                    log.info("文件上传成功: {}", objectName);
                } catch (Exception e) {
                    log.error("文件上传失败: {}", objectName, e);
                    return false;
                }
            }
        }

        return true;
    }

    /**
     * 列出指定文件夹中的所有文件和子文件夹
     *
     * @param folderPath 文件夹路径
     * @return 文件和子文件夹列表
     */
    public List<String> listFolderContent(String folderPath) {
        File folder = new File(folderPath);
        if (!folder.exists() || !folder.isDirectory()) {
            throw new IllegalArgumentException("指定的路径不是一个有效的文件夹");
        }

        File[] files = folder.listFiles();
        List<String> contents = new ArrayList<>();
        if (files != null) {
            for (File file : files) {
                contents.add(file.getName());
            }
        }

        return contents;
    }

    /**
     * 列出指定存储桶中的文件夹
     *
     * @param bucketName 存储桶名称
     * @param recursive  是否递归列出子文件夹
     * @return 文件夹名称列表
     */
    @SneakyThrows
    public List< String > listFolder(String bucketName, boolean recursive) {
        Iterable< Result< Item > > results = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).recursive(recursive).build());
        List< String > folders = new ArrayList<>();
        for (Result< Item > result : results) {
            Item item = result.get();
            if (item.isDir()) {
                folders.add(item.objectName());
            }
        }
        return folders;
    }

    /**
     * 判断文件夹是否存在
     *
     * @param bucketName 存储桶名称
     * @param folderName 文件夹名称
     * @return true 存在
     */
    @SneakyThrows
    public boolean doesFolderExist(String bucketName, String folderName) {
        if (bucketExists(bucketName)) {
            Iterable<Result<Item>> results = minioClient.listObjects(
                    ListObjectsArgs.builder()
                            .bucket(bucketName)
                            .prefix(folderName)
                            .recursive(false)
                            .build());
            for (Result< Item > result : results) {
                Item item = result.get();
                if (item.objectName().startsWith(folderName)) {
                    return true;
                }
            }
            return false;
        } else {
            log.warn("存储桶不存在: {}", bucketName);
            return false;
        }
    }

    /**
     * 创建文件夹或者目录
     *
     * @param bucketName 存储桶名称
     * @param folderName 目录路径
     */
    @SneakyThrows
    public boolean saveFolder(String bucketName, String folderName) {
        String folderPath = folderName.endsWith("/") ? folderName : folderName + "/";
        if (bucketExists(bucketName)) {
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(folderPath)
                    .stream(new ByteArrayInputStream(new byte[0]), 0, -1)
                    .build());
            return true;
        } else {
            log.warn("文件夹已存在: {}", folderName);
            return false;
        }
    }

    /**
     * 创建多个文件夹
     *
     * @param bucketName  存储桶名称
     * @param folderNames 目录路径列表
     * @return 成功创建的文件夹列表
     */
    @SneakyThrows
    public boolean saveFolder(String bucketName, List< String > folderNames) {
        if (bucketExists(bucketName)) {
            for (String folderName : folderNames) {
                String folderPath = folderName.endsWith("/") ? folderName : folderName + "/";
                try {
                    minioClient.putObject(PutObjectArgs.builder()
                            .bucket(bucketName)
                            .object(folderPath)
                            .stream(new ByteArrayInputStream(new byte[0]), 0, -1)
                            .build());
                } catch (Exception e) {
                    log.error("创建文件夹失败: {}", folderPath, e);
                    return false;
                }
            }
            return true;
        } else {
            log.warn("存储桶不存在: {}", bucketName);
            return false;
        }
    }


    /**
     * 删除文件夹
     *
     * @param bucketName 存储桶名称
     * @param folderName 文件名称
     * @return
     */
    @SneakyThrows
    public boolean removeFolder(String bucketName, String folderName) {
        String objectName = folderName.endsWith("/") ? folderName : folderName + "/";
        if (bucketExists(bucketName)) {
            Iterable< Result< Item > > results = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).prefix(objectName).build());
            for (Result< Item > result : results) {
                Item item = result.get();
                minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(item.objectName()).build());
            }
            return true;
        } else {
            log.warn("存储桶不存在: {}", bucketName);
            return false;
        }
    }

    /**
     * 批量删除文件夹
     *
     * @param bucketName  存储桶名称
     * @param folderNames 要删除的文件夹名称列表
     * @return 如果所有文件夹都成功删除返回true，否则返回false
     */
    @SneakyThrows
    public boolean removeFolder(String bucketName, List< String > folderNames) {
        if (!bucketExists(bucketName)) {
            log.warn("存储桶不存在: {}", bucketName);
            return false;
        }
        for (String folderName : folderNames) {
            if (!removeFolder(bucketName, folderName)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 修改文件夹名称
     *
     * @param bucketName    存储桶名称
     * @param oldFolderName 旧文件夹名称
     * @param newFolderName 新文件夹名称
     * @return 如果成功返回true，否则返回false
     */
    @SneakyThrows
    public boolean renameFolder(String bucketName, String oldFolderName, String newFolderName) {
        if (!bucketExists(bucketName)) {
            log.warn("存储桶不存在: {}", bucketName);
            return false;
        }
        if (!oldFolderName.endsWith("/")) {
            oldFolderName += "/";
        }
        if (!newFolderName.endsWith("/")) {
            newFolderName += "/";
        }
        Iterable< Result< Item > > results = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).prefix(oldFolderName).recursive(true).build());
        for (Result< Item > result : results) {
            Item item = result.get();
            String objectName = item.objectName();
            String newObjectName = objectName.replaceFirst(oldFolderName, newFolderName);
            minioClient.copyObject(
                    CopyObjectArgs.builder()
                            .bucket(bucketName)
                            .object(newObjectName)
                            .source(CopySource.builder().bucket(bucketName).object(objectName).build())
                            .build());
            minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
        }
        return true;
    }

    /**
     * 批量修改文件夹名称
     *
     * @param bucketName     存储桶名称
     * @param oldFolderNames 旧文件夹名称列表
     * @param newFolderNames 新文件夹名称列表
     * @return 是否成功
     */
    @SneakyThrows
    public boolean renameFolder(String bucketName, List< String > oldFolderNames, List< String > newFolderNames) {
        if (oldFolderNames.size() != newFolderNames.size()) {
            log.warn("旧文件夹名称列表和新文件夹名称列表长度不一致");
            return false;
        }

        if (!bucketExists(bucketName)) {
            log.warn("存储桶不存在: {}", bucketName);
            return false;
        }

        for (int i = 0; i < oldFolderNames.size(); i++) {
            String oldFolderName = oldFolderNames.get(i);
            String newFolderName = newFolderNames.get(i);

            if (!doesFolderExist(bucketName, oldFolderName)) {
                log.warn("文件夹不存在: {}", oldFolderName);
                return false;
            }

            try {
                // 列出文件夹中的所有文件
                Iterable< Result< Item > > results = minioClient.listObjects(
                        ListObjectsArgs.builder()
                                .bucket(bucketName)
                                .prefix(oldFolderName)
                                .recursive(true)
                                .build());

                for (Result< Item > result : results) {
                    Item item = result.get();
                    if (!item.isDir()) {
                        String oldObjectName = item.objectName();
                        String newObjectName = newFolderName + oldObjectName.substring(oldFolderName.length());

                        // 复制文件到新路径
                        minioClient.copyObject(
                                CopyObjectArgs.builder()
                                        .bucket(bucketName)
                                        .object(newObjectName)
                                        .source(CopySource.builder()
                                                .bucket(bucketName)
                                                .object(oldObjectName)
                                                .build())
                                        .build());

                        // 删除旧文件
                        minioClient.removeObject(
                                RemoveObjectArgs.builder()
                                        .bucket(bucketName)
                                        .object(oldObjectName)
                                        .build());
                    }
                }

                log.info("文件夹重命名成功: {} -> {}", oldFolderName, newFolderName);
            } catch (Exception e) {
                log.error("文件夹重命名失败: {}", oldFolderName, e);
                return false;
            }
        }

        return true;
    }

    /**
     * 获取文件夹大小
     *
     * @param bucketName 存储桶名称
     * @param folderName 文件夹名称
     * @return 文件夹大小，单位为字节，如果文件夹不存在或获取失败返回 -1
     */
    @SneakyThrows
    public long getFolderSize(String bucketName, String folderName) {
        if (!bucketExists(bucketName)) {
            log.warn("存储桶不存在: {}", bucketName);
            return -1;
        }
        if (!folderName.endsWith("/")) {
            folderName += "/";
        }
        Iterable< Result< Item > > results = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).prefix(folderName).recursive(true).build());
        long size = 0;
        for (Result< Item > result : results) {
            Item item = result.get();
            if (!item.isDir()) {
                StatObjectResponse statObject = statObject(bucketName, item.objectName());
                if (statObject != null) {
                    size += statObject.size();
                }
            }
        }
        return size;
    }

    /**
     * 列出文件夹层级结构
     *
     * @param bucketName 存储桶名称
     * @param folderName 文件夹名称
     * @return 以树形结构展示的文件夹层级信息，如果文件夹不存在返回空Map
     */
    @SneakyThrows
    public Map< String, Object > listFolderHierarchy(String bucketName, String folderName) {
        if (!bucketExists(bucketName)) {
            log.warn("存储桶不存在: {}", bucketName);
            return new HashMap<>();
        }
        if (!folderName.endsWith("/")) {
            folderName += "/";
        }
        Map< String, Object > hierarchy = new HashMap<>();
        Iterable< Result< Item > > results = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).prefix(folderName).recursive(true).build());
        for (Result< Item > result : results) {
            Item item = result.get();
            String objectName = item.objectName().substring(folderName.length());
            if (objectName.isEmpty()) {
                continue;
            }
            String[] parts = objectName.split("/");
            Map< String, Object > currentLevel = hierarchy;
            for (int i = 0; i < parts.length; i++) {
                String part = parts[i];
                if (i == parts.length - 1) {
                    if (item.isDir()) {
                        currentLevel.put(part, new HashMap<>());
                    } else {
                        currentLevel.put(part, null);
                    }
                } else {
                    Map< String, Object > subMap = (Map< String, Object >) currentLevel.get(part);
                    if (subMap == null) {
                        subMap = new HashMap<>();
                        currentLevel.put(part, subMap);
                    }
                    currentLevel = subMap;
                }
            }
        }
        return hierarchy;
    }

    /**
     * 移动文件夹
     *
     * @param sourceBucket    存储桶名称
     * @param sourceFolder 源文件夹路径（以 / 结尾）
     * @param targetBucket 目标存储桶
     * @param targetFolder 目标文件夹路径（以 / 结尾）
     * @return 如果成功返回true，否则返回false
     */
    @SneakyThrows
    public boolean moveFolder(String sourceBucket, String sourceFolder, String targetBucket, String targetFolder) {
        if (bucketExists(sourceBucket)) {
            sourceFolder = StrUtil.removePrefix(sourceFolder, "/");
            targetFolder = StrUtil.removePrefix(targetFolder, "/");
            sourceFolder = StrUtil.addSuffixIfNot(sourceFolder, "/");
            targetFolder = StrUtil.addSuffixIfNot(targetFolder, "/");

            // 判断sourceFolder目录是否存在
            Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder()
                    .bucket(sourceBucket)
                    .prefix(sourceFolder)
                    .recursive(true)
                    .build());

            List<CompletableFuture<Void>> futures = new ArrayList<>();
            String normalizedPath = FileUtil.normalize(sourceFolder);
            String lastPath = FileUtil.getName(normalizedPath) + "/";

            // 遍历结果并复制文件到新位置
            for (Result<Item> result : results) {
                Item item = result.get();
                String objectName = item.objectName();
                String newObjectName = FileUtil.normalize(targetFolder + lastPath + objectName.substring(sourceFolder.length()));

                // 复制文件到新路径
                CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                    try {
                        minioClient.copyObject(CopyObjectArgs.builder()
                                .bucket(targetBucket)
                                .object(newObjectName)
                                .source(CopySource.builder().bucket(sourceBucket).object(objectName).build())
                                .build());
                        minioClient.removeObject(RemoveObjectArgs.builder()
                                .bucket(sourceBucket)
                                .object(objectName)
                                .build());
                    } catch (Exception e) {
                        System.out.println("Error during move operation: " + e.getMessage());
                    }
                });
                futures.add(future);
            }

            // 等待所有异步操作完成
            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get();
            return true;

        } else {
            log.warn("存储桶不存在: {}", sourceBucket);
            return false;
        }
    }

    /**
     * 批量移动文件夹（源文件夹和目标文件夹一一对应）
     *
     * @param sourceBucket   源存储桶
     * @param sourceFolders  源文件夹列表
     * @param targetBucket   目标存储桶
     * @param targetFolders  目标文件夹列表
     * @return 如果成功返回 true，否则返回 false
     */
    @SneakyThrows
    public boolean moveFolder(String sourceBucket, List<String> sourceFolders, String targetBucket, List<String> targetFolders) {
        if (bucketExists(sourceBucket)) {

            // 用于存储所有异步任务
            List<CompletableFuture<Void>> futures = new ArrayList<>();

            // 遍历源文件夹和目标文件夹
            for (int i = 0; i < sourceFolders.size(); i++) {
                // 确保路径以 / 结尾，并去掉前导斜杠
                String sourceFolder = sourceFolders.get(i);
                String targetFolder = targetFolders.get(i);
                sourceFolder = StrUtil.removePrefix(sourceFolder, "/");
                targetFolder = StrUtil.removePrefix(targetFolder, "/");
                sourceFolder = StrUtil.addSuffixIfNot(sourceFolder, "/");
                targetFolder = StrUtil.addSuffixIfNot(targetFolder, "/");

                String normalizedPath = FileUtil.normalize(sourceFolder);
                String lastPath = FileUtil.getName(normalizedPath) + "/";

                // 列出源文件夹中的所有对象
                Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder()
                        .bucket(sourceBucket)
                        .prefix(sourceFolder)
                        .recursive(true)
                        .build());

                // 遍历结果并复制文件到新位置
                for (Result<Item> result : results) {
                    Item item = result.get();
                    String objectName = item.objectName();
                    String newObjectName = targetFolder + lastPath + objectName.substring(sourceFolder.length());

                    // 异步复制文件到新路径
                    CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                        try {
                            minioClient.copyObject(CopyObjectArgs.builder()
                                    .bucket(targetBucket)
                                    .object(newObjectName)
                                    .source(CopySource.builder()
                                            .bucket(sourceBucket)
                                            .object(objectName)
                                            .build())
                                    .build());

                            // 删除旧文件
                            minioClient.removeObject(RemoveObjectArgs.builder()
                                    .bucket(sourceBucket)
                                    .object(objectName)
                                    .build());

                            log.info("移动对象: {} -> {}", objectName, newObjectName);
                        } catch (Exception e) {
                            log.error("移动对象失败: {} -> {}", objectName, newObjectName, e);
                        }
                    });

                    futures.add(future);
                }
            }
            // 等待所有异步操作完成
            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
            log.info("文件夹批量移动完成: {} -> {}", sourceFolders, targetFolders);
            return true;
        } else {
            log.warn("存储桶不存在: {}", sourceBucket);
            return false;
        }
    }


    /**
     * 下载文件夹为压缩包
     *
     * @param bucketName    存储桶名称
     * @param folderName    文件夹名称
     * @param localFilePath 本地保存压缩包的路径
     * @return 如果成功返回true，否则返回false
     */
    @SneakyThrows
    public boolean downloadFolderAsZip(String bucketName, String folderName, String localFilePath) {
        if (bucketExists(bucketName)) {
            if (!folderName.endsWith("/")) {
                folderName += "/";
            }
            Iterable< Result< Item > > results = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).prefix(folderName).recursive(true).build());
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ZipOutputStream zos = new ZipOutputStream(bos);
            for (Result< Item > result : results) {
                Item item = result.get();
                String objectName = item.objectName();
                InputStream is = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());
                ZipEntry zipEntry = new ZipEntry(objectName.replaceFirst(folderName, ""));
                zos.putNextEntry(zipEntry);
                byte[] buffer = new byte[1024];
                int length;
                while ((length = is.read(buffer)) != -1) {
                    zos.write(buffer, 0, length);
                }
                is.close();
                zos.closeEntry();
            }
            zos.close();
            byte[] zipBytes = bos.toByteArray();
            FileOutputStream fos = new FileOutputStream(new File(localFilePath));
            fos.write(zipBytes);
            fos.close();
            return true;
        } else {
            log.warn("存储桶不存在: {}", bucketName);
            return false;
        }
    }

    /**
     * 拷贝文件夹
     *
     * @param srcBucketName  源存储桶名称
     * @param srcFolderName  源文件夹名称（包含路径）
     * @param destBucketName 目标存储桶名称
     * @param destFolderName 目标文件夹名称（包含路径）
     * @return 如果成功返回true，否则返回false
     */
    @SneakyThrows
    public boolean copyFolder(String srcBucketName, String srcFolderName, String destBucketName, String destFolderName) {
        if (bucketExists(srcBucketName) || bucketExists(destBucketName)) {
            if (!doesFolderExist(srcBucketName, srcFolderName)) {
                log.warn("源文件夹不存在");
                return false;
            }
            if (!doesFolderExist(destBucketName, destFolderName)) {
                saveFolder(destBucketName, destFolderName);
            }
            Iterable< Result< Item > > results = minioClient.listObjects(ListObjectsArgs.builder()
                    .bucket(srcBucketName)
                    .prefix(srcFolderName)
                    .recursive(true)
                    .build());

            for (Result< Item > result : results) {
                Item item = result.get();
                if (!item.isDir()) {
                    String srcObjectName = item.objectName();
                    String relativePath = srcObjectName.substring(srcFolderName.length());
                    String destObjectName = destFolderName + relativePath;
                    minioClient.copyObject(CopyObjectArgs.builder().bucket(destBucketName).object(destObjectName).source(CopySource.builder().bucket(srcBucketName).object(srcObjectName).build()).build());
                }
            }
            return true;
        } else {
            log.warn("源存储桶或目标存储桶不存在");
            return false;
        }
    }

    /**
     * 批量拷贝文件夹
     *
     * @param srcBucketName  源存储桶名称
     * @param srcFolderNames 源文件夹名称列表
     * @param destBucketName 目标存储桶名称
     * @param destFolderName 目标文件夹名称
     * @return 如果成功返回true，否则返回false
     */
    public boolean copyFolder(String srcBucketName, List< String > srcFolderNames, String destBucketName, String destFolderName) {
        if (bucketExists(srcBucketName) && bucketExists(destBucketName)) {
            try {
                for (String sourceFolder : srcFolderNames) {
                    // 列出源文件夹中的所有文件
                    Iterable< Result< Item > > results = minioClient.listObjects(
                            ListObjectsArgs.builder()
                                    .bucket(srcBucketName)
                                    .prefix(sourceFolder)
                                    .recursive(true)
                                    .build());

                    for (Result< Item > result : results) {
                        Item item = result.get();
                        if (!item.isDir()) { // 只处理文件，忽略文件夹
                            String sourceFilePath = item.objectName();
                            String targetFilePath = destFolderName + "/" + sourceFilePath.substring(sourceFolder.length());

                            // 复制文件
                            minioClient.copyObject(
                                    CopyObjectArgs.builder()
                                            .bucket(destBucketName)
                                            .object(targetFilePath)
                                            .source(CopySource.builder()
                                                    .bucket(srcBucketName)
                                                    .object(sourceFilePath)
                                                    .build())
                                            .build());
                        }
                    }
                }
            } catch (MinioException | InvalidKeyException | NoSuchAlgorithmException | IOException e) {
                System.out.println("复制文件夹时发生错误: " + e.getMessage());
                return false;
            }
            return true;
        } else {
            log.warn("源存储桶或目标存储桶不存在");
            return false;
        }
    }

    /**
     * 批量下载文件夹并保存为本地压缩包
     *
     * @param bucketName   存储桶名称
     * @param folderNames  文件夹名称列表
     * @param localZipPath 本地压缩包保存路径
     * @return 是否成功保存压缩包
     */
    @SneakyThrows
    public boolean downloadFoldersAsZipAndSave(String bucketName, List< String > folderNames, String localZipPath) {
        if (!bucketExists(bucketName)) {
            log.warn("存储桶不存在: {}", bucketName);
            return false;
        }

        try (ZipArchiveOutputStream zipOut = new ZipArchiveOutputStream(new FileOutputStream(localZipPath))) {
            for (String folderName : folderNames) {
                // 列出文件夹中的所有文件
                Iterable< Result< Item > > results = minioClient.listObjects(ListObjectsArgs.builder()
                        .bucket(bucketName)
                        .prefix(folderName)
                        .recursive(true)
                        .build());

                for (Result< Item > result : results) {
                    Item item = result.get();
                    if (!item.isDir()) { // 只处理文件，忽略文件夹
                        // 获取文件元数据
                        StatObjectResponse statObject = minioClient.statObject(StatObjectArgs.builder()
                                .bucket(bucketName)
                                .object(item.objectName())
                                .build());

                        if (statObject == null || statObject.size() <= 0) {
                            continue; // 跳过空文件
                        }

                        // 下载文件并写入 ZIP
                        try (InputStream inputStream = minioClient.getObject(GetObjectArgs.builder()
                                .bucket(bucketName)
                                .object(item.objectName())
                                .build())) {
                            String entryName = item.objectName().substring(folderName.length());
                            ZipArchiveEntry zipEntry = new ZipArchiveEntry(entryName);
                            zipOut.putArchiveEntry(zipEntry);

                            byte[] buffer = new byte[1024];
                            int length;
                            while ((length = inputStream.read(buffer)) > 0) {
                                zipOut.write(buffer, 0, length);
                            }

                            zipOut.closeArchiveEntry();
                        }
                    }
                }
            }

            zipOut.finish(); // 完成 ZIP 文件写入
            return true; // 操作成功
        } catch (IOException e) {
            log.error("压缩文件或保存压缩包时发生错误", e);
            return false; // 操作失败
        } catch (MinioException | InvalidKeyException | NoSuchAlgorithmException e) {
            log.error("从MinIO获取文件时发生错误", e);
            return false; // 操作失败
        }
    }

    /**
     * 根据文件前置查询文件
     *
     * @param bucketName 存储桶名称
     * @param prefix     前缀
     * @param recursive  是否递归查询
     * @return MinioItem 列表
     */
    @SneakyThrows
    public List< Item > getAllObjectsByPrefix(String bucketName, String prefix, boolean recursive) {
        Iterable< Result< Item > > results = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).prefix(prefix).recursive(recursive).build());
        List< Item > items = new ArrayList<>();
        for (Result< Item > result : results) {
            Item item = result.get();
            if (item.isDir() == recursive) {
                items.add(item);
            }
        }
        return items;
    }

    /**
     * 查询文件名称列表
     *
     * @param bucketName 存储桶名称
     * @param prefix     前缀（用于模糊查询）
     * @return 文件名称列表
     */
    @SneakyThrows
    public List< String > listFileNames(String bucketName, String prefix) {
        if (!bucketExists(bucketName)) {
            log.warn("存储桶不存在: {}", bucketName);
            return Collections.emptyList();
        }

        List< String > fileNames = new ArrayList<>();
        Iterable< Result< Item > > results = minioClient.listObjects(
                ListObjectsArgs.builder()
                        .bucket(bucketName)
                        .prefix(prefix)
                        .recursive(false)
                        .build());

        for (Result< Item > result : results) {
            Item item = result.get();
            if (!item.isDir()) { // 只处理文件
                fileNames.add(item.objectName());
            }
        }

        return fileNames;
    }

    /**
     * 查询单个文件
     *
     * @param bucketName 存储桶名称
     * @param objectName 文件名称
     * @return 文件信息
     */
    @SneakyThrows
    public Map< String, Object > getFileInfo(String bucketName, String objectName) {
        if (!bucketExists(bucketName)) {
            log.warn("存储桶不存在: {}", bucketName);
            return null;
        }

        try {
            StatObjectResponse stat = minioClient.statObject(
                    StatObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .build());

            Map< String, Object > fileInfo = new HashMap<>();
            fileInfo.put("objectName", objectName);
            fileInfo.put("size", stat.size());
            fileInfo.put("lastModified", stat.lastModified());
            return fileInfo;
        } catch (Exception e) {
            log.error("查询文件失败: {}", objectName, e);
            return null;
        }
    }

    /**
     * 上传文件
     *
     * @param file       要上传的文件
     * @param bucketName 存储桶名称
     * @param fileType   文件类型
     * @return 上传后的文件访问路径
     */
    @SneakyThrows
    public String uploadFile(String bucketName, MultipartFile file, String fileType) {
        bucketName = StringUtils.isNotBlank(bucketName) ? bucketName : minIOConfigProperties.getBucket();

        if (!bucketExists(bucketName)) {
            saveBucket(bucketName);
        }
        String fileName = file.getOriginalFilename();
        String objectName = UUID.randomUUID().toString().replaceAll("-", "") + fileName.substring(fileName.lastIndexOf("."));
        minioClient.putObject(
                PutObjectArgs.builder()
                        .bucket(bucketName)
                        .object(objectName)
                        .stream(file.getInputStream(), file.getSize(), -1)
                        .contentType(fileType)
                        .build());
        return minIOConfigProperties.getReadPath() + separator + bucketName + separator + objectName;
    }

    /**
     * 上传文件到文件夹
     *
     * @param bucketName 存储桶名称
     * @param folderName 文件夹名称（包含路径）
     * @param fileName   文件名称
     * @param file     文件
     * @return 是否成功
     */
    @SneakyThrows
    public boolean uploadFileToFolder(String bucketName, String folderName, String fileName, MultipartFile file) {
        if (bucketExists(bucketName)) {
            String objectName = FileUtil.normalize(folderName + "/" + fileName);
            try {
                minioClient.putObject(
                        PutObjectArgs.builder()
                                .bucket(bucketName)
                                .object(objectName)
                                .stream(file.getInputStream(), file.getSize(), -1) // 10MB 分片
                                .build());
                log.info("文件上传成功: {}", objectName);
            } catch (Exception e) {
                log.error("文件上传失败: {}", objectName, e);
                return false;
            }
            return true;
        } else {
            log.warn("存储桶不存在: {}", bucketName);
            return false;
        }
    }

    /**
     * 批量上传文件到文件夹
     *
     * @param bucketName 存储桶名称
     * @param folderName 文件夹名称（包含路径）
     * @param files      文件列表（文件名 -> 文件流）
     * @return 是否成功
     */
    @SneakyThrows
    public boolean uploadFileToFolder(String bucketName, String folderName, Map< String, MultipartFile > files) {
        if (bucketExists(bucketName)) {
            for (Map.Entry< String, MultipartFile > entry : files.entrySet()) {
                String fileName = entry.getKey();
                MultipartFile file = entry.getValue();
                String objectName = FileUtil.normalize(folderName + "/" + fileName);
                try {
                    minioClient.putObject(
                            PutObjectArgs.builder()
                                    .bucket(bucketName)
                                    .object(objectName)
                                    .stream(file.getInputStream(), file.getSize(), -1) // 10MB 分片
                                    .build());
                    log.info("文件上传成功: {}", objectName);
                } catch (Exception e) {
                    log.error("文件上传失败: {}", objectName, e);
                    return false;
                }
            }
            return true;
        } else {
            log.warn("存储桶不存在: {}", bucketName);
            return false;
        }
    }

    /**
     * 上传多个文件
     *
     * @param files      多个文件
     * @param bucketName 存储名称
     * @param fileType   文件类型
     * @return 文件路径
     */
    @SneakyThrows
    public List< String > uploadFile(List< MultipartFile > files, String bucketName, String fileType) {
        bucketName = StringUtils.isNotBlank(bucketName) ? bucketName : minIOConfigProperties.getBucket();

        if (!bucketExists(bucketName)) {
            saveBucket(bucketName);
        }

        List< String > uploadedFileUrls = new ArrayList<>();
        for (MultipartFile file : files) {
            String fileName = file.getOriginalFilename();
            String objectName = UUID.randomUUID().toString().replaceAll("-", "") + fileName.substring(fileName.lastIndexOf("."));
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .stream(file.getInputStream(), file.getSize(), -1)
                            .contentType(fileType)
                            .build());
            uploadedFileUrls.add(minIOConfigProperties.getReadPath() + separator + bucketName + separator + objectName);
        }
        return uploadedFileUrls;
    }

    /**
     * 预览文件（支持多种文件类型）
     *
     * @param bucketName 存储桶名称
     * @param objectName 文件名称
     * @return 文件内容（文本或文件的 Base64 编码）
     */
    @SneakyThrows
    public String previewFile(String bucketName, String objectName) {
        if (!bucketExists(bucketName)) {
            log.warn("存储桶不存在: {}", bucketName);
            return null;
        }

        try (InputStream stream = minioClient.getObject(
                GetObjectArgs.builder()
                        .bucket(bucketName)
                        .object(objectName)
                        .build())) {

            byte[] data = StreamUtils.copyToByteArray(stream);

            // 获取文件扩展名
            String extension = objectName.substring(objectName.lastIndexOf(".") + 1).toLowerCase();

            // 根据文件类型处理
            switch (extension) {
                // 文本文件
                case "txt":
                case "csv":
                case "json":
                case "xml":
                case "html":
                case "css":
                case "js":
                    return new String(data, StandardCharsets.UTF_8);

                // 图片文件
                case "jpg":
                case "jpeg":
                case "png":
                case "gif":
                case "bmp":
                case "webp":
                    return Base64.getEncoder().encodeToString(data);

                // PDF 文件
                case "pdf":
//                    return Base64.getEncoder().encodeToString(data);
                    try (PDDocument document = Loader.loadPDF((RandomAccessRead) new ByteArrayInputStream(data))) {
                        PDFTextStripper stripper = new PDFTextStripper();
                        return stripper.getText(document); // 返回 PDF 文件的文本内容
                    } catch (Exception e) {
                        log.error("解析 PDF 文件失败", e);
                        return "无法解析 PDF 文件内容。";
                    }
                    // Word 文件
                case "docx":
                    try (XWPFDocument doc = new XWPFDocument(new ByteArrayInputStream(data))) {
                        XWPFWordExtractor extractor = new XWPFWordExtractor(doc);
                        return extractor.getText(); // 返回 Word 文件内容
                    } catch (Exception e) {
                        log.error("解析 Word 文件失败", e);
                        return "无法解析 Word 文件内容。";
                    }

                    // Excel 文件
                case "xlsx":
                    try (XSSFWorkbook workbook = new XSSFWorkbook(new ByteArrayInputStream(data))) {
                        StringBuilder content = new StringBuilder();
                        for (Sheet sheet : workbook) {
                            for (Row row : sheet) {
                                for (Cell cell : row) {
                                    content.append(cell.toString()).append("\t");
                                }
                                content.append("\n");
                            }
                        }
                        return content.toString(); // 返回 Excel 文件内容
                    } catch (Exception e) {
                        log.error("解析 Excel 文件失败", e);
                        return "无法解析 Excel 文件内容。";
                    }

                    // PowerPoint 文件
                case "pptx":
                    try (HSLFSlideShow ppt = new HSLFSlideShow(new ByteArrayInputStream(data))) {
                        StringBuilder content = new StringBuilder();
                        for (HSLFSlide slide : ppt.getSlides()) {
                            content.append(slide.getTitle()).append("\n");
                        }
                        return content.toString(); // 返回 PPT 文件内容
                    } catch (Exception e) {
                        log.error("解析 PPT 文件失败", e);
                        return "无法解析 PPT 文件内容。";
                    }

                    // 其他文件类型
                default:
                    log.warn("不支持的文件类型: {}", objectName);
                    return null;
            }
        } catch (Exception e) {
            log.error("文件预览失败: {}", objectName, e);
            return null;
        }
    }

    /**
     * 拷贝文件
     *
     * @param bucketName    存储桶名称
     * @param objectName    文件名称
     * @param srcBucketName 源存储桶名称
     * @param srcObjectName 目标文件名称
     * @return 如果成功返回true，否则返回false
     */
    @SneakyThrows
    public boolean copyFile(String bucketName, String objectName, String srcBucketName, String srcObjectName) {
        if (bucketExists(bucketName)) {
            minioClient.copyObject(
                    CopyObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .source(CopySource.builder().bucket(srcBucketName).object(srcObjectName).build())
                            .build());
            return true;
        } else {
            log.warn("存储桶不存在: {}", bucketName);
            return false;
        }
    }

    /**
     * 批量拷贝文件
     *
     * @param bucketName    存储桶名称--目标
     * @param objectNames   文件名称
     * @param srcBucketName 源存储桶名称
     * @param srcObjectName 目标文件名称
     * @return 如果成功返回true，否则返回false
     */
    public boolean copyFile(String bucketName, List< String > objectNames, String srcBucketName, String srcObjectName) {
        if (bucketExists(bucketName)) {
            for (String objectName : objectNames) {
                try {
                    minioClient.copyObject(
                            CopyObjectArgs.builder()
                                    .bucket(bucketName)
                                    .object(objectName)
                                    .source(CopySource.builder()
                                            .bucket(srcBucketName)
                                            .object(srcObjectName)
                                            .build())
                                    .build()
                    );
                } catch (MinioException e) {
                    log.error("复制文件时发生Minio错误: {}", e.getMessage(), e);
                    return false;
                } catch (Exception e) {
                    log.error("发生未知错误");
                    return false;
                }
            }
            return true;
        } else {
            log.warn("存储桶不存在: {}", bucketName);
            return false;
        }
    }


    /**
     * 修改文件名称
     *
     * @param bucketName 存储桶名称
     * @param oldName    旧文件名称
     * @param newName    新文件名称
     * @return 是否成功
     */
    @SneakyThrows
    public boolean renameFile(String bucketName, String oldName, String newName) {
        if (bucketExists(bucketName)) {
            try {
                minioClient.copyObject(
                        CopyObjectArgs.builder()
                                .bucket(bucketName)
                                .object(newName)
                                .source(CopySource.builder()
                                        .bucket(bucketName)
                                        .object(oldName)
                                        .build())
                                .build());

                minioClient.removeObject(
                        RemoveObjectArgs.builder()
                                .bucket(bucketName)
                                .object(oldName)
                                .build());

                log.info("文件重命名成功: {} -> {}", oldName, newName);
            } catch (Exception e) {
                log.error("文件重命名失败: {}", oldName, e);
                return false;
            }
            return true;
        } else {
            log.warn("存储桶不存在: {}", bucketName);
            return false;
        }
    }


    /**
     * 批量修改文件名称
     *
     * @param bucketName   存储桶名称
     * @param oldFileNames 旧文件名称列表
     * @param newFileNames 新文件名称列表
     * @return 是否成功
     */
    @SneakyThrows
    public boolean renameFile(String bucketName, List< String > oldFileNames, List< String > newFileNames) {
        if (oldFileNames.size() != newFileNames.size()) {
            log.warn("旧文件名称列表和新文件名称列表长度不一致");
            return false;
        }

        if (bucketExists(bucketName)) {
            for (int i = 0; i < oldFileNames.size(); i++) {
                String oldFileName = oldFileNames.get(i);
                String newFileName = newFileNames.get(i);

                try {
                    // 复制文件到新路径
                    minioClient.copyObject(
                            CopyObjectArgs.builder()
                                    .bucket(bucketName)
                                    .object(newFileName)
                                    .source(CopySource.builder()
                                            .bucket(bucketName)
                                            .object(oldFileName)
                                            .build())
                                    .build());

                    // 删除旧文件
                    minioClient.removeObject(
                            RemoveObjectArgs.builder()
                                    .bucket(bucketName)
                                    .object(oldFileName)
                                    .build());

                    log.info("文件重命名成功: {} -> {}", oldFileName, newFileName);
                } catch (Exception e) {
                    log.error("文件重命名失败: {}", oldFileName, e);
                    return false;
                }
            }
            return true;
        } else {
            log.warn("存储桶不存在: {}", bucketName);
            return false;
        }
    }

    /**
     * 获取文件访问路径
     *
     * @param bucketName 存储桶名称
     * @param objectName 对象名称
     * @return 文件访问路径
     */
    @SneakyThrows
    public String getFileAccessPath(String bucketName, String objectName) {
        if (bucketExists(bucketName)) {
            String url = minioClient.getPresignedObjectUrl(
                    GetPresignedObjectUrlArgs.builder()
                            .method(Method.GET)
                            .bucket(bucketName)
                            .object(objectName)
                            .expiry(2, TimeUnit.MINUTES)
                            .build());
            log.info("生成的预签名URL: {}", url);
            return url;
        } else {
            log.warn("存储桶不存在: {}", bucketName);
            return "";
        }
    }

    /**
     * 删除一个对象
     *
     * @param bucketName 存储桶名称
     * @param objectName 对象名称
     * @return 如果成功删除对象返回true，否则返回false
     */
    @SneakyThrows
    public boolean removeFile(String bucketName, String objectName) {
        if (bucketExists(bucketName)) {
            minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
            log.info("对象已删除: {}/{}", bucketName, objectName);
            return true;
        } else {
            log.warn("存储桶不存在: {}", bucketName);
            return false;
        }
    }

    /**
     * 以流的形式获取一个文件对象
     *
     * @param bucketName 存储桶名称
     * @param objectName 对象名称
     * @return 文件输入流
     */
    @SneakyThrows
    public InputStream downloadFile(String bucketName, String objectName) {
        if (bucketExists(bucketName)) {
            StatObjectResponse statObject = statObject(bucketName, objectName);
            if (statObject != null && statObject.size() > 0) {
                return minioClient.getObject(
                        GetObjectArgs.builder()
                                .bucket(bucketName)
                                .object(objectName)
                                .build());
            } else {
                log.warn("文件不存在或为空: {}/{}", bucketName, objectName);
            }
        }
        return null;
    }

    /**
     * 批量下载文件并打包为 ZIP 压缩包
     *
     * @param bucketName  存储桶名称
     * @param folderNames 文件夹名称列表
     * @param zipFilePath 本地 ZIP 文件保存路径
     * @return 是否成功打包并保存
     */
    public boolean downLoadFilesAsZipAndSave(String bucketName, List< String > folderNames, String zipFilePath) {
        // 检查存储桶是否存在
        if (bucketExists(bucketName)) {
            try (FileOutputStream fos = new FileOutputStream(zipFilePath);
                 BufferedOutputStream bos = new BufferedOutputStream(fos);
                 ZipOutputStream zos = new ZipOutputStream(bos)) {

                for (String folderName : folderNames) {
                    // 列出文件夹中的所有文件
                    Iterable< Result< Item > > results = minioClient.listObjects(
                            ListObjectsArgs.builder()
                                    .bucket(bucketName)
                                    .prefix(folderName)
                                    .recursive(true)
                                    .build());

                    for (Result< Item > result : results) {
                        Item item = result.get();
                        if (!item.isDir()) { // 只处理文件，忽略文件夹
                            String objectName = item.objectName();
                            String entryName = objectName.substring(folderName.length() + 1); // 去掉文件夹前缀

                            // 下载文件并写入 ZIP
                            try (InputStream objectStream = minioClient.getObject(
                                    GetObjectArgs.builder()
                                            .bucket(bucketName)
                                            .object(objectName)
                                            .build())) {

                                ZipEntry zipEntry = new ZipEntry(entryName);
                                zos.putNextEntry(zipEntry);

                                byte[] buffer = new byte[1024];
                                int length;
                                while ((length = objectStream.read(buffer)) > 0) {
                                    zos.write(buffer, 0, length);
                                }

                                zos.closeEntry();
                            }
                        }
                    }
                }
            } catch (FileNotFoundException e) {
                System.out.println("文件未找到: " + zipFilePath);
                return false;
            } catch (IOException e) {
                System.out.println("文件写入错误: " + e.getMessage());
                return false;
            } catch (MinioException | InvalidKeyException | NoSuchAlgorithmException e) {
                System.out.println("MinIO 操作错误: " + e.getMessage());
                return false;
            }
            return true;
        } else {
            System.out.println("存储桶不存在: " + bucketName);
            return false;
        }
    }

    /**
     * 获取对象的元数据
     *
     * @param bucketName 存储桶名称
     * @param objectName 对象名称
     * @return 对象的元数据
     */
    @SneakyThrows
    public StatObjectResponse statObject(String bucketName, String objectName) {
        if (bucketExists(bucketName)) {
            return minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());
        } else {
            log.warn("存储桶不存在: {}", bucketName);
            return null;
        }
    }

    /**
     * 删除指定桶的多个文件对象
     *
     * @param bucketName  存储桶名称
     * @param objectNames 要删除的对象名称列表
     * @return 如果所有对象都成功删除返回true，否则返回false
     */
    @SneakyThrows
    public boolean removeFile(String bucketName, List< String > objectNames) {
        if (bucketExists(bucketName)) {
            var deleteObjects = objectNames.stream().map(DeleteObject::new).toList();
            var results = minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(deleteObjects).build());

            for (var result : results) {
                DeleteError error = result.get();
                log.error("删除对象时出错 {}: {}", error.objectName(), error.message());
                return false;
            }
            log.info("所有对象已成功从存储桶中删除: {}", bucketName);
            return true;
        } else {
            log.warn("存储桶不存在: {}", bucketName);
            return false;
        }
    }

    /**
     * 移动文件
     *
     * @param bucketName    存储桶名称
     * @param objectName    文件名称
     * @param srcBucketName 源存储桶名称
     * @param srcObjectName 源文件名称
     * @return 如果成功移动文件返回true，否则返回false
     */
    @SneakyThrows
    public boolean moveFile(String bucketName, String objectName, String srcBucketName, String srcObjectName) {
        if (bucketExists(bucketName)) {
            minioClient.copyObject(
                    CopyObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .source(CopySource.builder().bucket(srcBucketName).object(srcObjectName).build())
                            .build());
            minioClient.removeObject(RemoveObjectArgs.builder().bucket(srcBucketName).object(srcObjectName).build());
            log.info("成功移动文件: {}/{} 到 {}/{}", bucketName, objectName, srcBucketName, srcObjectName);
            return true;
        } else {
            log.warn("存储桶不存在: {}", bucketName);
            return false;
        }
    }

    /**
     * 批量移动文件
     *
     * @param bucketName    存储桶名称
     * @param objectNames   文件名称
     * @param srcBucketName 源存储桶名称
     * @param srcObjectName 源文件名称
     * @return 如果成功移动文件返回true，否则返回false
     */
    @SneakyThrows
    public boolean moveFile(String bucketName, List< String > objectNames, String srcBucketName, String srcObjectName) {
        if (bucketExists(bucketName)) {
            for (String objectName : objectNames) {
                try {
                    minioClient.copyObject(
                            CopyObjectArgs.builder()
                                    .bucket(bucketName)
                                    .object(objectName)
                                    .source(CopySource.builder().bucket(srcBucketName).object(srcObjectName).build())
                                    .build());
                    minioClient.removeObject(RemoveObjectArgs.builder().bucket(srcBucketName).object(srcObjectName).build());
                } catch (Exception e) {
                    log.error("移动文件时出错: {}/{}", bucketName, objectName);
                    return false;
                }
            }
            return true;
        } else {
            log.warn("存储桶不存在: {}", bucketName);
            return false;
        }
    }

    /**
     * 获取文件的所有版本
     *
     * @param bucketName 存储桶名称
     * @param objectName 对象名称
     * @return 文件的所有版本信息列表，如果获取失败返回空列表
     */
    @SneakyThrows
    public List< Item > listObjectVersions(String bucketName, String objectName) {
        List< Item > versions = new ArrayList<>();
        Iterable< Result< Item > > results = minioClient.listObjects(ListObjectsArgs.builder()
                .bucket(bucketName)
                .prefix(objectName)
                .includeVersions(true)
                .build()
        );
        for (Result< Item > result : results) {
            versions.add(result.get());
        }
        return versions;
    }

    /**
     * 恢复文件到指定版本
     *
     * @param bucketName 存储桶名称
     * @param objectName 对象名称
     * @param versionId  版本ID
     * @return 如果成功返回true，否则返回false
     */
    @SneakyThrows
    public boolean restoreObjectVersion(String bucketName, String objectName, String versionId) {
        if (!bucketExists(bucketName)) {
            log.warn("存储桶不存在: {}", bucketName);
            return false;
        }
        InputStream inputStream = minioClient.getObject(GetObjectArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .versionId(versionId)
                .build());
        return putObject(bucketName, objectName, inputStream, "application/octet-stream");
    }

    /**
     * 为文件添加元数据
     *
     * @param bucketName 存储桶名称
     * @param objectName 对象名称
     * @param metadata   元数据（键值对形式）
     * @return 如果成功返回true，否则返回false
     */
    @SneakyThrows
    public boolean addObjectMetadata(String bucketName, String objectName, Map< String, String > metadata) {
        if (!bucketExists(bucketName)) {
            log.warn("存储桶不存在: {}", bucketName);
            return false;
        }
        // 获取对象的当前状态
        StatObjectResponse statObject = minioClient.statObject(
                StatObjectArgs.builder()
                        .bucket(bucketName)
                        .object(objectName)
                        .build()
        );

        // 准备新的对象参数，包括新的元数据
        PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .stream(minioClient.getObject(
                        GetObjectArgs.builder()
                                .bucket(bucketName)
                                .object(objectName)
                                .build()
                ), statObject.size(), -1)
                .contentType(statObject.contentType())
                .userMetadata(metadata)
                .build();

        // 上传带有新元数据的对象
        minioClient.putObject(putObjectArgs);
        return true;
    }

    /**
     * 获取文件的元数据
     *
     * @param bucketName 存储桶名称
     * @param objectName 对象名称
     * @return 文件的元数据（键值对形式），如果获取失败返回空Map
     */
    @SneakyThrows
    public Map< String, String > getObjectMetadata(String bucketName, String objectName) {
        StatObjectResponse stat = minioClient.statObject(
                StatObjectArgs.builder()
                        .bucket(bucketName)
                        .object(objectName)
                        .build()
        );
        return stat.userMetadata();
    }

    /**
     * 以流的形式获取一个文件对象（断点下载）
     *
     * @param bucketName 存储桶名称
     * @param objectName 对象名称
     * @param offset     起始字节的位置
     * @param length     要读取的长度 (可选，如果无值则代表读到文件结尾)
     * @return 文件输入流
     */
    @SneakyThrows
    public InputStream getObject(String bucketName, String objectName, long offset, Long length) {
        if (bucketExists(bucketName)) {
            StatObjectResponse statObject = statObject(bucketName, objectName);
            if (statObject != null && statObject.size() > 0) {
                return minioClient.getObject(
                        GetObjectArgs.builder()
                                .bucket(bucketName)
                                .object(objectName)
                                .offset(offset)
                                .length(length)
                                .build());
            }
            log.warn("对象不存在或为空: {}/{}", bucketName, objectName);
        }
        return null;
    }

    /**
     * 通过InputStream上传对象
     *
     * @param bucketName  存储桶名称
     * @param objectName  对象名称
     * @param inputStream 要上传的输入流
     * @param contentType 要上传的文件类型
     * @return 如果成功上传对象返回true，否则返回false
     */
    @SneakyThrows
    public boolean putObject(String bucketName, String objectName, InputStream inputStream, String contentType) {
        if (bucketExists(bucketName)) {
            minioClient.putObject(
                    PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(
                                    inputStream, -1, 1048576099)
                            .contentType(contentType)
                            .build());
            StatObjectResponse statObject = statObject(bucketName, objectName);
            if (statObject != null && statObject.size() > 0) {
                log.info("对象已上传: {}/{}", bucketName, objectName);
                return true;
            }
            log.warn("对象上传失败: {}/{}", bucketName, objectName);
        }
        return false;
    }


    /**
     * 获取对象的预签名URL（用于下载）
     * <p>
     * 此方法会检查指定的存储桶是否存在，如果不存在则创建该存储桶。
     * 然后生成一个预签名的URL，允许在一定时间内通过GET请求访问指定的对象。
     *
     * @param bucketName 存储桶名称
     * @param objectName 对象名称
     * @param headers    额外的查询参数（作为HTTP头信息传递）
     * @return 返回预签名的URL字符串
     */
    @SneakyThrows
    public String getPresignedObjectUrl(String bucketName, String objectName, Map< String, String > headers) {
        if (!bucketExists(bucketName)) {
            saveBucket(bucketName);
        }
        return minioClient.getPresignedObjectUrl(
                GetPresignedObjectUrlArgs.builder()
                        .bucket(bucketName)
                        .object(objectName)
                        .method(Method.GET)
                        .extraQueryParams(headers)
                        .build());
    }

    /**
     * 异步列出分片上传的部分信息
     * <p>
     * 该方法用于异步获取指定对象的分片上传部分列表。
     * 主要用于监控或管理大文件的分片上传过程。
     *
     * @param bucketName 存储桶名称
     * @param objectName 对象名称
     * @param uploadId   分片上传的唯一标识符
     * @return 返回包含分片信息的CompletableFuture<ListPartsResponse>
     */
    @SneakyThrows
    public CompletableFuture< ListPartsResponse > listPartsAsync(String bucketName, String objectName, String uploadId) {
        return minioAsyncClient.listPartsAsync(
                bucketName,
                null,
                objectName,
                null,
                null,
                uploadId,
                null,
                null
        );
    }

    /**
     * 获取对象的预签名URL（用于上传）
     * <p>
     * 生成一个预签名的URL，允许在指定时间内通过PUT请求上传对象到指定位置。
     * 可以设置额外的查询参数来控制上传行为。
     *
     * @param bucketName      存储桶名称
     * @param objectName      对象名称
     * @param expiryInSeconds URL的有效期（秒）
     * @param params          额外的查询参数
     * @return 返回预签名的URL字符串
     * @throws Exception 如果生成预签名URL失败，则抛出异常
     */
    @SneakyThrows
    public String getPresignedObjectUrl(String bucketName, String objectName, int expiryInSeconds, Map< String, String > params) throws Exception {
        return minioClient.getPresignedObjectUrl(
                GetPresignedObjectUrlArgs.builder()
                        .method(Method.PUT)
                        .bucket(bucketName)
                        .object(objectName)
                        .expiry(expiryInSeconds)
                        .extraQueryParams(params)
                        .build()
        );
    }

    /**
     * 完成分片上传操作
     * <p>
     * 该方法用于完成一个分片上传任务，将所有已上传的部分合并为一个完整的对象。
     * 使用异步方式执行，并返回一个CompletableFuture<ObjectWriteResponse>，
     * 允许调用者处理上传完成后的响应。
     *
     * @param bucketName 存储桶名称
     * @param objectName 对象名称
     * @param uploadId   分片上传的唯一标识符
     * @param parts      已上传的所有分片信息列表
     * @return 返回包含上传结果的CompletableFuture<ObjectWriteResponse>
     */
    @SneakyThrows
    public CompletableFuture< ObjectWriteResponse > completeMultipartUploadAsync(String bucketName, String objectName, String uploadId, List< Part > parts) {
        try {
            return minioAsyncClient.completeMultipartUploadAsync(
                    bucketName,
                    null,
                    objectName,
                    uploadId,
                    parts.stream()
                            .map(part -> new Part(
                                    part.partNumber(),
                                    part.etag()
                            ))
                            .toList().toArray(new Part[0]),
                    null,
                    null
            );
        } catch (InsufficientDataException | NoSuchAlgorithmException | InternalException | InvalidKeyException |
                 IOException | XmlParserException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 分页查询文件（支持模糊查询）
     *
     * @param bucketName 存储桶名称
     * @param prefix     前缀（用于模糊查询）
     * @param page       当前页码
     * @param size       每页大小
     * @return 分页结果
     */
    @SneakyThrows
    public Map< String, Object > listFilesWithPagination(String bucketName, String prefix, int page, int size) {
        if (!bucketExists(bucketName)) {
            log.warn("存储桶不存在: {}", bucketName);
            return null;
        }

        List< Map< String, Object > > files = new ArrayList<>();
        Iterable< Result< Item > > results = minioClient.listObjects(
                ListObjectsArgs.builder()
                        .bucket(bucketName)
                        .prefix(prefix)
                        .recursive(true)
                        .build());

        int total = 0;
        int start = (page - 1) * size;
        int end = start + size;

        for (Result< Item > result : results) {
            if (total >= end) break;

            Item item = result.get();
            if (!item.isDir()) { // 只处理文件
                if (total >= start) {
                    Map< String, Object > fileInfo = new HashMap<>();
                    fileInfo.put("objectName", item.objectName());
                    fileInfo.put("size", item.size());
                    fileInfo.put("lastModified", item.lastModified());
                    files.add(fileInfo);
                }
                total++;
            }
        }

        int pages = (int) Math.ceil((double) total / size);

        Map< String, Object > result = new HashMap<>();
        result.put("data", files);
        result.put("total", total);
        result.put("pageSize", size);
        result.put("pages", pages);
        result.put("page", page);
        return result;
    }

    /**
     * 分页查询文件夹（支持模糊查询）
     *
     * @param bucketName 存储桶名称
     * @param prefix     前缀（用于模糊查询）
     * @param page       当前页码
     * @param size       每页大小
     * @return 分页结果
     */
    @SneakyThrows
    public Map< String, Object > listFolderWithPagination(String bucketName, String prefix, int page, int size) {
        if (!bucketExists(bucketName)) {
            log.warn("存储桶不存在: {}", bucketName);
            return null;
        }

        List< Map< String, Object > > folders = new ArrayList<>();
        Iterable< Result< Item > > results = minioClient.listObjects(
                ListObjectsArgs.builder()
                        .bucket(bucketName)
                        .prefix(prefix)
                        .recursive(true)
                        .build());

        int total = 0;
        int start = (page - 1) * size;
        int end = start + size;

        for (Result< Item > result : results) {
            if (total >= end) break;

            Item item = result.get();
            if (item.isDir()) { // 只处理文件夹
                if (total >= start) {
                    Map< String, Object > folderInfo = new HashMap<>();
                    folderInfo.put("objectName", item.objectName());
                    folderInfo.put("lastModified", item.lastModified());
                    folders.add(folderInfo);
                }
                total++;
            }
        }

        int pages = (int) Math.ceil((double) total / size);

        Map< String, Object > result = new HashMap<>();
        result.put("data", folders);
        result.put("total", total);
        result.put("pageSize", size);
        result.put("pages", pages);
        result.put("page", page);
        return result;
    }
}